package org.ctd.springframework.beans.exception.bean.instantiation;

import com.sun.istack.internal.Nullable;
import org.ctd.springframework.beans.exception.bean.fatal.FatalBeanException;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * BeanInstantiationException
 *
 * @author chentudong
 * @date 2020/9/11 16:12
 * @since 1.0
 */
public class BeanInstantiationException extends FatalBeanException {
    private final Class<?> beanClass;

    @Nullable
    private Constructor<?> constructor;

    @Nullable
    private Method constructingMethod;

    /**
     * Create a new BeanInstantiationException.
     *
     * @param beanClass the offending bean class
     * @param msg       the detail message
     */
    public BeanInstantiationException(Class<?> beanClass, String msg) {
        this(beanClass, msg, null);
    }

    /**
     * Create a new BeanInstantiationException.
     *
     * @param beanClass the offending bean class
     * @param msg       the detail message
     * @param cause     the root cause
     */
    public BeanInstantiationException(Class<?> beanClass, String msg, @Nullable Throwable cause) {
        super("Failed to instantiate [" + beanClass.getName() + "]: " + msg, cause);
        this.beanClass = beanClass;
    }

    public <T> BeanInstantiationException(Constructor<?> constructor, String msg, @Nullable Throwable cause) {
        super("Failed to instantiate [" + constructor.getDeclaringClass().getName() + "]: " + msg, cause);
        this.beanClass = constructor.getDeclaringClass();
        this.constructor = constructor;
    }

    /**
     * Create a new BeanInstantiationException.
     *
     * @param constructingMethod the delegate for bean construction purposes
     *                           (typically, but not necessarily, a static factory method)
     * @param msg                the detail message
     * @param cause              the root cause
     * @since 4.3
     */
    public BeanInstantiationException(Method constructingMethod, String msg, @Nullable Throwable cause) {
        super("Failed to instantiate [" + constructingMethod.getReturnType().getName() + "]: " + msg, cause);
        this.beanClass = constructingMethod.getReturnType();
        this.constructingMethod = constructingMethod;
    }

    public Class<?> getBeanClass() {
        return beanClass;
    }

    @Nullable
    public Constructor<?> getConstructor() {
        return constructor;
    }

    @Nullable
    public Method getConstructingMethod() {
        return constructingMethod;
    }
}
